﻿using cocos2d;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace cocos2d_xna.cocoa
{
    public static class CCPointExtensions
    {
        public static float ccpLengthSQ(CCPoint v)
        {
            return ccpDot(v, v);
        }

        public static float ccpDot(CCPoint v1, CCPoint v2)
        {
            return v1.x * v2.x + v1.y * v2.y;
        }

        public static CCPoint ccp(float x, float y)
        {
            return new CCPoint(x, y);
        }

        public static float ccpLength(CCPoint v)
        {
            return (float)Math.Sqrt(ccpLengthSQ(v));
        }


        public static float ccpDistance(CCPoint v1, CCPoint v2)
        {
            return ccpLength(ccpSub(v1, v2));
        }

        public static CCPoint ccpSub(CCPoint v1, CCPoint v2)
        {
            return ccp(v1.x - v2.x, v1.y - v2.y);
        }

        public static CCPoint ccpNormalize(CCPoint v)
        {
            return ccpMult(v, 1.0f / ccpLength(v));
        }


        public static CCPoint ccpMult(CCPoint v, float s)
        {
            return ccp(v.x * s, v.y * s);
        }

        public static CCPoint ccpForAngle(float a)
        {
            return ccp((float)Math.Cos(a), (float)Math.Sin(a));
        }


        public static float ccpToAngle(CCPoint v)
        {
            return (float)Math.Atan2(v.y, v.x);
        }

        public static CCPoint ccpLerp(CCPoint a, CCPoint b, float alpha)
        {
            return ccpAdd(ccpMult(a, 1.0f - alpha), ccpMult(b, alpha));
        }


        public static CCPoint ccpAdd(CCPoint v1, CCPoint v2)
        {
            return ccp(v1.x + v2.x, v1.y + v2.y);
        }

        public static float clampf(float value, float min_inclusive, float max_inclusive)
        {
            if (min_inclusive > max_inclusive)
            {
                Swap(ref min_inclusive, ref max_inclusive);
            }
            return value < min_inclusive ? min_inclusive : value < max_inclusive ? value : max_inclusive;
        }

        public static void Swap(ref float a, ref float b)
        {
            float temp = a;
            a = b;
            b = temp;
        }

        public static CCPoint ccpClamp(CCPoint p, CCPoint min_inclusive, CCPoint max_inclusive)
        {
            return ccp(clampf(p.x, min_inclusive.x, max_inclusive.x), clampf(p.y, min_inclusive.y, max_inclusive.y));
        }

        public static CCPoint ccpFromSize(CCSize s)
        {
            return ccp(s.width, s.height);
        }

        public static CCPoint ccpCompOp(CCPoint p, Func<float, float> opFunc)
        {
            return ccp(opFunc(p.x), opFunc(p.y));
        }

        public static bool ccpFuzzyEqual(CCPoint a, CCPoint b, float var)
        {
            if (a.x - var <= b.x && b.x <= a.x + var)
                if (a.y - var <= b.y && b.y <= a.y + var)
                    return true;
            return false;
        }

        public static CCPoint ccpCompMult(CCPoint a, CCPoint b)
        {
            return ccp(a.x * b.x, a.y * b.y);
        }

        public static float ccpAngleSigned(CCPoint a, CCPoint b)
        {
            CCPoint a2 = ccpNormalize(a);
            CCPoint b2 = ccpNormalize(b);
            float angle = (float)Math.Atan2(a2.x * b2.y - a2.y * b2.x, ccpDot(a2, b2));
            if (Math.Abs(angle) < float.Epsilon) return 0.0f;
            return angle;
        }

        public static CCPoint ccpRotateByAngle(CCPoint v, CCPoint pivot, float angle)
        {
            CCPoint r = ccpSub(v, pivot);
            float cosa = (float)Math.Cos(angle), sina = (float)Math.Sin(angle);
            float t = r.x;
            r.x = t * cosa - r.y * sina + pivot.x;
            r.y = t * sina + r.y * cosa + pivot.y;
            return r;
        }


        public static bool ccpSegmentIntersect(CCPoint A, CCPoint B, CCPoint C, CCPoint D)
        {
            float S = 0;
            float T = 0;

            if (ccpLineIntersect(A, B, C, D, ref S, ref T)
                && (S >= 0.0f && S <= 1.0f && T >= 0.0f && T <= 1.0f))
                return true;

            return false;
        }

        public static CCPoint ccpIntersectPoint(CCPoint A, CCPoint B, CCPoint C, CCPoint D)
        {
            float S = 0;
            float T = 0;

            if (ccpLineIntersect(A, B, C, D, ref S, ref T))
            {
                // Point of intersection
                CCPoint P = new CCPoint();
                P.x = A.x + S * (B.x - A.x);
                P.y = A.y + S * (B.y - A.y);
                return P;
            }

            return CCPoint.CCPointZero;
        }

        public static bool ccpLineIntersect(CCPoint A, CCPoint B,
                              CCPoint C, CCPoint D,
                              ref float S, ref float T)
        {
            // FAIL: Line undefined
            if ((A.x == B.x && A.y == B.y) || (C.x == D.x && C.y == D.y))
            {
                return false;
            }
            float BAx = B.x - A.x;
            float BAy = B.y - A.y;
            float DCx = D.x - C.x;
            float DCy = D.y - C.y;
            float ACx = A.x - C.x;
            float ACy = A.y - C.y;

            float denom = DCy * BAx - DCx * BAy;

            S = DCx * ACy - DCy * ACx;
            T = BAx * ACy - BAy * ACx;

            if (denom == 0)
            {
                if (S == 0 || T == 0)
                {
                    // Lines incident
                    return true;
                }
                // Lines parallel and not incident
                return false;
            }

            S = S / denom;
            T = T / denom;

            // Point of intersection
            // CGPoint P;
            // P.x = A.x + *S * (B.x - A.x);
            // P.y = A.y + *S * (B.y - A.y);

            return true;
        }

        public static float ccpAngle(CCPoint a, CCPoint b)
        {
            float angle = (float)Math.Cos(ccpDot(ccpNormalize(a), ccpNormalize(b)));
            if (Math.Abs(angle) < float.Epsilon) return 0.0f;
            return angle;
        }
    }
}
